from crawler_core.constant.constants import Constants
from crawler_core.constant.constants_account import ConstantAccount
from crawler_core.exception.aoount_exec import AccountException
from crawler_core.exception.vertify_exec import VertifyException
from crawler_core.proxy.login_proxy import LoginProxy
from crawler_core.utils.redis_utils import RedisUtils
from lib.sys.logger_factory import LoggerFactory


class TaskLoginDown:
	logger = LoggerFactory.getLogger("TaskLogin", "login")

	@staticmethod
	def do_login(task_id, context):
		_type = context[Constants.CONTEXT_TYPE]
		corp_code = context[Constants.CONTEXT_CORP_CODE]
		username = context[Constants.CONTEXT_USERNAME]
		# 当前任务，某个渠道，存在的所有账号 list_account
		list_account = context[Constants.CONTEXT_ACCOUNT_LIST]
		a_size = len(list_account)
		# obj = RedisUtils.getEventAccount(corp_code, _type)，获取当前企业，某个渠道所有账号，状态是否可用的存储对象
		# 对象状态分为（账号密码错误=0，可登录=1，点数不够=2）
		obj = RedisUtils.getEventAccount(corp_code, _type)
		# 初始化-默认状态
		status = ConstantAccount.STATUS_DEFAULT
		if obj:
			# 从对象中，获取username的存储状态
			status = obj.get(username)
		# 开始判断状态
		# 1、当前状态，账号错误，需要登录可用账号
		if status == ConstantAccount.STATUS_ACCOUNT_ERROR:
			# a_size 表示 当前企业账号只有一个
			if a_size == 1:
				# 当前企业账号只有一个且错误账号，直接结束任务
				return {
					ConstantAccount.STATUS: ConstantAccount.STATUS_ACCOUNT_ERROR,
					ConstantAccount.LOGIN_STATUS: ConstantAccount.LOGIN_STATUS_NO_ACCOUNT
				}
			else:
				# 如果 obj 存在表示已登录验证过，因此，获取对象所有账户数组 keys = obj.keys()，比较当前任务账户集合list_account大小，
				# len(keys) == a_size，表示，当前账号是最后一个循环登录账号，无可用账号，直接结束任务
				if obj:
					keys = obj.keys()
					if len(keys) == a_size:
						# 无账号可用，直接结束任务
						return {
							ConstantAccount.STATUS: ConstantAccount.STATUS_ACCOUNT_ERROR,
							ConstantAccount.LOGIN_STATUS: ConstantAccount.LOGIN_STATUS_NO_ACCOUNT
						}
					else:
						# 存在可用账号，直接登录下一个账号
						return TaskLoginDown.login_channel_next(task_id, context, corp_code, _type, username)
				# 如果 obj 不存在，表示没有任何账号登录且验证过，需要使用当前账号登录并完成验证
				else:
					# 先登录当前账号
					l_data = TaskLoginDown.login_channel(task_id, context)
					# 判断登录结果，
					if l_data.get(ConstantAccount.STATUS) == ConstantAccount.STATUS_ACCOUNT_ERROR:
						# l_data.get(ConstantAccount.STATUS)登录失败，先标记当前账号有问题，然后继续执行登录(下一个账号)
						RedisUtils.addEventAccount(corp_code, _type, username, ConstantAccount.STATUS_ACCOUNT_ERROR)
						return TaskLoginDown.do_login(task_id, context)
					elif l_data.get(ConstantAccount.STATUS) == ConstantAccount.STATUS_ACCOUNT_OK:
						# 登录成功，直接返回
						RedisUtils.addEventAccount(corp_code, _type, username, ConstantAccount.STATUS_ACCOUNT_OK)
						l_data[ConstantAccount.LOGIN_STATUS] = ConstantAccount.LOGIN_STATUS_SUCCESS
						return l_data
					else:
						# 异常--登录失败，直接返回
						return {
							ConstantAccount.STATUS: ConstantAccount.STATUS_ACCOUNT_ERROR,
							ConstantAccount.LOGIN_STATUS: ConstantAccount.LOGIN_STATUS_EXCEPTION
						}
		else:
			if status == ConstantAccount.STATUS_DEFAULT or status == ConstantAccount.STATUS_COOKIES_LOSE:
				# 2、当前账号没有记录有效性(新开始的任务),或者cookies失效， 开始执行登录
				l_data = TaskLoginDown.login_channel(task_id, context)
				# 判断登录结果，
				if l_data.get(ConstantAccount.STATUS) == ConstantAccount.STATUS_ACCOUNT_ERROR:
					# l_data.get(ConstantAccount.STATUS_ACCOUNT_ERROR)登录失败，先标记当前账号有问题，然后继续执行登录(下一个账号)
					RedisUtils.addEventAccount(corp_code, _type, username, ConstantAccount.STATUS_ACCOUNT_ERROR)
					return TaskLoginDown.do_login(task_id, context)
				elif l_data.get(ConstantAccount.STATUS) == ConstantAccount.STATUS_ACCOUNT_OK:
					# 登录成功，直接返回
					RedisUtils.addEventAccount(corp_code, _type, username, ConstantAccount.STATUS_ACCOUNT_OK)
					l_data[ConstantAccount.LOGIN_STATUS] = ConstantAccount.LOGIN_STATUS_SUCCESS
					return l_data
				else:
					# 异常--登录失败，直接返回
					return {
						ConstantAccount.STATUS: ConstantAccount.STATUS_ACCOUNT_ERROR,
						ConstantAccount.LOGIN_STATUS: ConstantAccount.LOGIN_STATUS_EXCEPTION
					}
			else:
				# 3、存在可用 cookies 登录
				# 3.1 判断是否点数不够登录
				if status == ConstantAccount.STATUS_ACCOUNT_NO_COUNT:
					# 直接登录下一个账号
					return TaskLoginDown.login_channel_next(task_id, context, corp_code, _type, username)

				# 直接返回 当前有效 cookies
				return {
					ConstantAccount.STATUS: ConstantAccount.STATUS_ACCOUNT_OK,
					ConstantAccount.LOGIN_STATUS: ConstantAccount.LOGIN_STATUS_SUCCESS
				}

	@staticmethod
	def login_channel_next(task_id, context, corp_code, _type, username):
		# 获取下一个登录账号
		num = TaskLoginDown.get_next_account(username, context)
		if num == 1:
			# 执行登录
			l_data = TaskLoginDown.login_channel(task_id, context)
			# 判断登录结果
			if l_data.get(ConstantAccount.STATUS) == ConstantAccount.STATUS_ACCOUNT_ERROR:
				# l_data.get(ConstantAccount.STATUS_ACCOUNT_ERROR)登录失败，先标记当前账号有问题，然后继续执行登录(下一个账号)
				RedisUtils.addEventAccount(corp_code, _type, username, ConstantAccount.STATUS_ACCOUNT_ERROR)
				TaskLoginDown.do_login(task_id, context)
			elif l_data.get(ConstantAccount.STATUS) == ConstantAccount.STATUS_ACCOUNT_OK:
				# 登录成功，直接返回
				RedisUtils.addEventAccount(corp_code, _type, username, ConstantAccount.STATUS_ACCOUNT_OK)
				l_data[ConstantAccount.LOGIN_STATUS] = ConstantAccount.LOGIN_STATUS_SUCCESS
				return l_data
			else:
				# 异常--登录失败，直接返回
				return {
					ConstantAccount.STATUS: ConstantAccount.STATUS_ACCOUNT_ERROR,
					ConstantAccount.LOGIN_STATUS: ConstantAccount.LOGIN_STATUS_EXCEPTION
				}
		else:
			# 无账号可用，直接结束任务
			return {
				ConstantAccount.STATUS: ConstantAccount.STATUS_ACCOUNT_ERROR,
				ConstantAccount.LOGIN_STATUS: ConstantAccount.LOGIN_STATUS_NO_ACCOUNT
			}

	@staticmethod
	def login_channel(task_id, context):
		data = {ConstantAccount.STATUS: ConstantAccount.STATUS_ACCOUNT_OK}
		proxy = LoginProxy()
		try:
			cookies = proxy.login(context)
			data[ConstantAccount.COOKIES] = cookies
		except VertifyException:
			TaskLoginDown.logger.info("登录任务 -- [%s] --执行登录, 验证码错误，任务结束..." % task_id)
			data[ConstantAccount.STATUS] = ConstantAccount.STATUS_VERIFY_ERROR
		except AccountException:
			TaskLoginDown.logger.info("登录任务 -- [%s] --执行登录, 账号密码错误, 重新其他账号登录 ..." % task_id)
			data[ConstantAccount.STATUS] = ConstantAccount.STATUS_ACCOUNT_ERROR
		return data

	@staticmethod
	def get_next_account(username, context):
		list_account = context[Constants.CONTEXT_ACCOUNT_LIST]
		index = 0
		for account in list_account:
			if username == account.get(Constants.CONTEXT_USERNAME):
				break
			index += 1
		# 获取下一个账户的坐标
		index += 1
		# 过滤数组越界
		if len(list_account) >= index:
			account = list_account[index]
			context[Constants.CONTEXT_USERNAME] = account.get(Constants.CONTEXT_USERNAME)
			context[Constants.CONTEXT_PASSWORD] = account.get(Constants.CONTEXT_PASSWORD)
			context[Constants.CONTEXT_ACCOUNT] = account.get(Constants.CONTEXT_ACCOUNT)
			return 1
		return 0